/**
 *    Copyright (C) 2008, 2009  Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_MODELS_IDX_MAP_MODEL_MODEL_API_HPP
#define _CUBEFS_CORE_MODELS_IDX_MAP_MODEL_MODEL_API_HPP

#include <cubefs/core/models/idx_map/names.hpp>
#include <cubefs/api/models/logs.hpp>

#define CUBEFS_SOURCE "cubefs/core/models/idx_map/model/model_api.hpp:" CUBEFS_AT

namespace cubefs    {
namespace ctr       {


CUBEFS_MODEL_PART_BEGIN(cubefs::models::idx_map::ModelApiName)

    typedef typename Base::Types                                                Types;
    typedef typename Base::PageMgr                                              PageMgr;

    typedef typename Base::Page                                                 Page;
    typedef typename Base::ID                                                   ID;


    typedef typename Base::NodeBase                                             NodeBase;
    typedef typename Base::Counters                                             Counters;
    typedef typename Base::Iterator                                             Iterator;

    typedef typename Base::NodeDispatcher                                       NodeDispatcher;
    typedef typename Base::RootDispatcher                                       RootDispatcher;
    typedef typename Base::LeafDispatcher                                       LeafDispatcher;
    typedef typename Base::NonLeafDispatcher                                    NonLeafDispatcher;

    typedef typename Base::Node2RootMap                                         Node2RootMap;
    typedef typename Base::Root2NodeMap                                         Root2NodeMap;

    typedef typename Base::NodeFactory                                          NodeFactory;

    typedef typename Base::Key                                                  Key;
    typedef typename Base::Value                                                Value;

    typedef typename Base::ApiKeyType                                           ApiKeyType;
    typedef typename Base::ApiValueType                                         ApiValueType;

    static const Int Indexes                                                    = Base::Indexes;

    virtual cubefs::api::Iterator* Find(Map::Pair* pair, Int search_type)
    {
        if (pair == NULL) throw NullPointerException(CUBEFS_SOURCE, "pair must not be null");

        StaticListOfTyped* keys = TypedCast<StaticListOfTyped>(pair->key());

        Int i = pair->GetIdx();

        ApiKeyType* item = TypedCast<ApiKeyType>(keys->Get(i));

        BigInt index = item->GetValue();

        if (search_type == cubefs::api::IdxMap::LT) {
            return new Iterator(me_.FindLT(index, i, false));
        }
        else if (search_type == cubefs::api::IdxMap::LE) {
            return new Iterator(me_.FindLE(index, i, false));
        }
        else {
            throw CubefsException(CUBEFS_SOURCE, "Invalid search_type value", search_type);
        }
    }

    bool Find(BigInt index, Int c, Int search_type, Typed *value)
    {
        ApiValueType* v = TypedCast<ApiValueType>(value);
        if (search_type == cubefs::api::IdxMap::LT) {
            return find_lt(index, c, v);
        }
        else if (search_type == cubefs::api::IdxMap::LE) {
            return find_le(index, c, v);
        }
        else {
            throw CubefsException(CUBEFS_SOURCE, "Invalid search_type value", search_type);
        }
    }

    virtual bool Get(Map::Pair *pair)
    {
        return Get(pair, cubefs::api::TreeMap::LE);
    }

    virtual bool Get(Map::Pair *pair, Int search_type)
    {
        if (pair == NULL) throw NullPointerException(CUBEFS_SOURCE, "pair must not be null");

        StaticListOfTyped* keys = TypedCast<StaticListOfTyped>(pair->key());
        ApiValueType* value = TypedCast<ApiValueType>(pair->value());

        Int idx = pair->GetIdx();
        ApiKeyType* key_item   = TypedCast<ApiKeyType>(keys->Get(idx));
        
        Key key = key_item->GetValue();

        return Find(key, idx, search_type, value);
    }

    virtual void Put(Map::Pair *pair)
    {
        if (pair == NULL) throw NullPointerException(CUBEFS_SOURCE, "pair must not be null");

        StaticListOfTyped* keys = TypedCast<StaticListOfTyped>(pair->key());
        ApiValueType* value = TypedCast<ApiValueType>(pair->value());

        me_.Insert(keys, value, pair->GetIdx());
    }

    void Insert(StaticListOfTyped* indexes, Typed *value, Int idx)
    {
        if (indexes == NULL) throw NullPointerException(CUBEFS_SOURCE, "Indexes must not be null");
        if (indexes->size() != Indexes) throw BoundsException(CUBEFS_SOURCE, "Invalid indexes list size", 0, indexes->size(), Indexes);

        Key keys[Indexes];

        for (Int c = 0; c < Indexes; c++) {
            keys[c] = TypedCast<ApiKeyType>(indexes->Get(c))->GetValue();
        }

        Iterator i = me_.FindLE(keys[idx], idx, true);
        ApiValueType* v = TypedCast<ApiValueType>(value);

        for (Int c = 0; c < Indexes; c++) keys[c] -= i.prefix(c);

        if (!i.IsEmpty()) {
            CUBEFS_TRACE(me_, "Insert into", i.page()->id(), "at", i.key_idx(), "key", keys[0], "prefix", i.prefix(0));
        }
        else {
            CUBEFS_TRACE(me_, "Insert into empty map", keys[0]);
        }

        if (i.IsEof() || i.IsEmpty()) {
            me_.InsertEntry(i, keys, v->GetValue());
        }
        else if (keys[0] == i.GetKey(0)) {
            me_.SetLeafData(i.page(), i.key_idx(), v->GetValue());
        }
        else {
            me_.InsertEntry(i, keys, v->GetValue());
        }
    }
    
    virtual bool Remove(Map::Pair *pair)
    {
        if (pair == NULL) throw NullPointerException(CUBEFS_SOURCE, "pair must not be null");

        StaticListOfTyped* keys = TypedCast<StaticListOfTyped>(pair->key());
        Int idx = pair->GetIdx();
        ApiKeyType* k   = TypedCast<ApiKeyType>(keys->Get(idx));

        return Remove(k->GetValue());
    }

    bool Remove(BigInt index)
    {
        Iterator i = me_.FindLE(index, 0, false);
        return me_.RemoveEntry(i);
    }

    virtual bool Remove(Map::Pair* from, Map::Pair* to)
    {
        if (from == NULL) throw NullPointerException(CUBEFS_SOURCE, "from must not be null");
        if (to   == NULL) throw NullPointerException(CUBEFS_SOURCE, "to must not be null");

        if (from->GetIdx() != to->GetIdx()) throw CubefsException(CUBEFS_SOURCE, "from.idx != to.idx");

        StaticListOfTyped* from_keys  = TypedCast<StaticListOfTyped>(from->key());
        StaticListOfTyped* to_keys    = TypedCast<StaticListOfTyped>(to->key());

        Int idx                 = from->GetIdx();

        ApiKeyType* from_item   = TypedCast<ApiKeyType>(from_keys->Get(idx));
        ApiKeyType* to_item     = TypedCast<ApiKeyType>(to_keys->Get(idx));

        BigInt from_idx         = from_item->GetValue();
        BigInt to_idx           = to_item->GetValue();

        return me_.RemoveAllEntries(from_idx, to_idx, idx, cubefs::api::IdxMap::LE);
    }

private:
    bool find_lt(BigInt index, Int c, Typed *value) {
        Iterator i = me_.FindLT(index, c, false);

        if (!i.IsFound()) {
            return false;
        }
        else {
            Value val = i.GetData();
            ApiValueType* v = TypedCast<ApiValueType>(value);
            TypedValueHelper<typename ApiValueType::InterfaceType>::set(v, val);
            return true;
        }
    }

    bool find_le(BigInt index, Int c, Typed *value) {
        Iterator i = me_.FindLE(index, c, false);

        if (!i.IsFound()) {
            return false;
        }
        else {
            Value val = i.GetData();
            ApiValueType* v = TypedCast<ApiValueType>(value);
            TypedValueHelper<typename ApiValueType::InterfaceType>::set(v, val);
            return true;
        }
    }
   
CUBEFS_MODEL_PART_END

}
}

#undef CUBEFS_SOURCE

#endif //_CUBEFS_CORE_MODELS_VECTOR_MODEL_MAP_API_HPP
